import bpy
import os

bl_info = {
    "name" : "One Key Tools",
    "author" : "FuZhiBo",
    "version" : (0,0,1),
    "blender" : (2, 61, 0),
    "location" : "Object > One Key Tools",
    "description" : """use one key to make some
                         object faster and easer.""",
    "warning" : "",
    "wiki_url" : "http://git.oschina.net/mobilefzb/blender_plug_mode_factory",
    "category" : "Object",
}

#define a one key operator parent
class OneKeyOperator(bpy.types.Operator):
    """one key operator parent"""
    bl_idname = "object.one_key_operator"
    bl_label = "one key operator"
    @classmethod
    def poll(cls,context):
        if context.active_object is not None :
            obj = context.active_object
            if obj.mode == 'OBJECT' and bpy.data.scenes["Scene"].render.engine == 'CYCLES' :
                return True
            else :
                return False
        else :
            return False

#define a operator
class PlaneBecomeCottonOperator(OneKeyOperator):
    """The operator about how to make a plane become a cotton."""
    bl_idname = "object.plane_cotton_operator"
    bl_label = "Plane Become Cotton Object Operator"

    def execute(self, context):
        bpy.ops.object.shade_smooth()
        bpy.ops.object.mode_set(mode="EDIT")
        #subdivide
        bpy.ops.mesh.subdivide(number_cuts=50)
        #back to OBJECT mode
        bpy.ops.object.mode_set(mode="OBJECT")
        #bind materials
        cottonMaterial = context.blend_data.materials.new("CottonMaterial")
        bpy.ops.object.material_slot_add()
        context.active_object.material_slots[0].material = cottonMaterial
        # Set Up the Cotton Material
        cottonMaterial.name = "CottonMaterial"
        #how to set value under cycles mode
        mat = bpy.data.materials.get(cottonMaterial.name)
        if mat is None :
            #if it does not exist create it
            mat = bpy.data.materials.new(cottonMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        #get node in nodes
        for node in nodes :
            if node.name == 'Diffuse BSDF' :
                print("remove it")
                mat.node_tree.nodes.remove(node)
            elif node.name == 'Material Output' :
                print("it is output material")
                matOutputShader = node
        #maybe we add a new output material shader
        if matOutputShader is None :
            #we will add a material output node
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (300,0)
        #add a glossy shader
        glossyBSDFNode = nodes.new('ShaderNodeBsdfGlossy')
        glossyBSDFNode.location = (100,0)
        #make links
        links.new(glossyBSDFNode.outputs['BSDF'],
                matOutputShader.inputs['Surface'])
        #adjust node args
        glossyBSDFNode.inputs['Roughness'].default_value = 0.800
        #NodeSocketColor have 4 zeros
        glossyBSDFNode.inputs['Color'].default_value = (0.8,0.212,0.0,1.0)
        #add modifier
        bpy.ops.object.modifier_add(type='CLOTH')
        bpy.ops.object.modifier_add(type='SOLIDIFY')
        bpy.ops.object.modifier_add(type='SUBSURF')
        #set cotton is difficute we should describe the file path
        bpy.ops.script.execute_preset(filepath=tmp_cotton_file_path,
                                    menu_idname="CLOTH_MT_presets")
        #set modifier
        #set cloth simulate quality
        context.active_object.modifiers['Cloth'].settings.quality = 20
        #set cloth collision
        context.active_object.modifiers['Cloth'].collision_settings.use_collision = True
        #set cloth collision quality
        context.active_object.modifiers['Cloth'].collision_settings.collision_quality = 5
        #set cloth self collision
        context.active_object.modifiers['Cloth'].collision_settings.use_self_collision = True
        #set cloth self collision quality
        context.active_object.modifiers['Cloth'].collision_settings.self_collision_quality = 5
        #set solidify quality
        context.active_object.modifiers['Solidify'].thickness = 0.03
        #set subsurf quality
        context.active_object.modifiers['Subsurf'].levels = 2
        return {'FINISHED'}

class GlassCupObjectOperator(OneKeyOperator):
    """The Operator about how to make a cup model become a glass cup.
        Please make sure you are changing a cup model."""
    bl_idname = "object.glass_cup_operator"
    bl_label = "Make Glass Cup Operator"

    def execute(self,context):
        #first we make object shade smooth
        bpy.ops.object.shade_smooth()
        #add glass material
        glassMaterial = context.blend_data.materials.new("GlassMaterial")
        bpy.ops.object.material_slot_add()#bind galss material to object
        context.active_object.material_slots[0].material = glassMaterial
        #set up material
        glassMaterial.name = "GlassMaterial"
        mat = bpy.data.materials.get(glassMaterial.name)
        if mat is None :
            mat = bpy.data.materials.new(glassMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        for node in nodes:
            if node.name == 'Diffuse BSDF':
                print("remove it")
                mat.node_tree.nodes.remove(node)
            elif node.name == 'Material Output':
                print("it is output material")
                matOutputShader = node
        if matOutputShader is None :
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (600,120)
        #add a glass shader
        glassBSDFNode = nodes.new('ShaderNodeBsdfGlass')
        glassBSDFNode.location = (100,0)
        #add a glossy shader
        glossyBSDFNode = nodes.new('ShaderNodeBsdfGlossy')
        glossyBSDFNode.location = (100,200)
        #add a mix shader
        mixShaderNode = nodes.new('ShaderNodeMixShader')
        mixShaderNode.location = (400,120)
        #these codes is ugly
        links.new(glossyBSDFNode.outputs['BSDF'],
                    mixShaderNode.inputs[1])
        links.new(glassBSDFNode.outputs['BSDF'],
                    mixShaderNode.inputs[2])
        links.new(mixShaderNode.outputs['Shader'],
                    matOutputShader.inputs['Surface'])
        glossyBSDFNode.inputs['Roughness'].default_value = 0.100
        mixShaderNode.inputs['Fac'].default_value = 0.9
        #add modifiers solidify and smooth
        bpy.ops.object.modifier_add(type='SOLIDIFY')
        bpy.ops.object.modifier_add(type='SUBSURF')
        bpy.ops.object.modifier_add(type='COLLISION')
        #set solidify quality
        context.active_object.modifiers['Solidify'].thickness = 0.08
        #set subsurf quality
        context.active_object.modifiers['Subsurf'].levels = 2
        return {'FINISHED'}

class JellyObjectOperator(OneKeyOperator):
    """The operator about a jelly object create
        add a jelly material nodes automatic on 
        current selected object."""
    bl_idname = "object.jelly_operator"
    bl_label = "Make Jelly Operator"

    def execute(self,context):
        jellyMaterial = context.blend_data.materials.new("JellyMaterial")
        bpy.ops.object.material_slot_add()
        context.active_object.material_slots[0].material = jellyMaterial

        jellyMaterial.name = "JellyMaterial"
        mat = bpy.data.materials.get(jellyMaterial.name)
        if mat is None :
            mat = bpy.data.materials.new(jellyMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        for node in nodes:
            if node.name == 'Diffuse BSDF':
                print("remove it")
                mat.node_tree.nodes.remove(node)
            elif node.name == 'Material Output':
                print("it is output material")
                matOutputShader = node
        #adjust output material
        if matOutputShader is  None:
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (800,120)
        #add subsurface scattering
        subsurfaceScatteringNode = nodes.new('ShaderNodeSubsurfaceScattering')
        subsurfaceScatteringNode.location = (100,0)
        #add glossy bsdf
        glossyBSDFNode = nodes.new('ShaderNodeBsdfGlossy')
        glossyBSDFNode.location = (100,200)
        #add glass bsdf
        glassBSDFNode = nodes.new('ShaderNodeBsdfGlass')
        glassBSDFNode.location = (100,400)
        #add mix shader
        mixShaderSubGlossNode = nodes.new('ShaderNodeMixShader')
        mixShaderSubGlossNode.location = (400,120)
        mixShaderMixGlasNode = nodes.new('ShaderNodeMixShader')
        mixShaderMixGlasNode.location = (600,220)
        #make links
        links.new(subsurfaceScatteringNode.outputs['BSSRDF'],
                            mixShaderSubGlossNode.inputs[2])
        links.new(glossyBSDFNode.outputs['BSDF'],
                    mixShaderSubGlossNode.inputs[1])
        links.new(mixShaderSubGlossNode.outputs['Shader'],
                            mixShaderMixGlasNode.inputs[2])
        links.new(glassBSDFNode.outputs['BSDF'],
                            mixShaderMixGlasNode.inputs[1])
        links.new(mixShaderMixGlasNode.outputs['Shader'],
                            matOutputShader.inputs['Surface'])
        #ajust some arguments
        subsurfaceScatteringNode.inputs['Scale'].default_value = 0.8
        glossyBSDFNode.inputs['Roughness'].default_value = 0.0
        mixShaderSubGlossNode.inputs['Fac'].default_value = 0.83
        mixShaderMixGlasNode.inputs['Fac'].default_value = 0.396

        return {'FINISHED'}

class stainlessSteelObjectOperator(OneKeyOperator):
    """The operator about stainless stell object create
        add a stainless stell material automatic on current
        selected object"""
    bl_idname = "object.stainless_stell_operator"
    bl_label = "Make Stainless Stell"

    def execute(self,context):
        stainlessSteelMaterial = context.blend_data.materials.new("StainlessSteelMaterial")
        bpy.ops.object.material_slot_add()
        context.active_object.material_slots[0].material = stainlessSteelMaterial
        stainlessSteelMaterial.name = "StainlessSteelMaterial"
        mat = bpy.data.materials.get(stainlessSteelMaterial.name)
        if mat is None :
            mat = bpy.data.materials.new(stainlessSteelMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        for node in nodes:
            if node.name == 'Diffuse BSDF':
                print("remove it")
                mat.node_tree.nodes.remove(node)
            elif node.name == 'Material Output':
                print("it is output material")
                matOutputShader = node
        #adjust output material
        if matOutputShader is None :
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (600,120)
        #add gloss bsdf
        glossyBSDFNode = nodes.new('ShaderNodeBsdfGlossy')
        glossyBSDFNode.location = (100,0)
        #add anisotropic bsdf
        anisotropicBSDFNode = nodes.new('ShaderNodeBsdfAnisotropic')
        anisotropicBSDFNode.location = (100,200)
        #add mix shader
        mixShaderNode = nodes.new('ShaderNodeMixShader')
        mixShaderNode.location = (400,120)
        #make links
        links.new(glossyBSDFNode.outputs['BSDF'],mixShaderNode.inputs[2])
        links.new(anisotropicBSDFNode.outputs['BSDF'],mixShaderNode.inputs[1])
        links.new(mixShaderNode.outputs['Shader'],matOutputShader.inputs['Surface'])
        #adjust some arguments
        glossyBSDFNode.inputs['Roughness'].default_value = 0.038
        anisotropicBSDFNode.inputs['Roughness'].default_value = 0.138
        anisotropicBSDFNode.inputs['Anisotropy'].default_value = 0.572
        anisotropicBSDFNode.inputs['Rotation'].default_value = 0.032
        mixShaderNode.inputs['Fac'].default_value = 0.66

        return {'FINISHED'}

# one key to create a lether system,the function need a leather
# texture first!
class leatherObjectOperator(OneKeyOperator):
    """The operator about leather object create
        add a leather material automatic on current
        selected object"""
    bl_idname = "object.leather_operator"
    bl_label = "Make Leather"

    def execute(self,context):
        leatherMaterial = context.blend_data.materials.new("LeatherMaterial")
        bpy.ops.object.material_slot_add()
        context.active_object.material_slots[0].material = leatherMaterial
        leatherMaterial.name = "LeatherMaterial"
        mat = bpy.data.materials.get(leatherMaterial.name)
        if mat is None :
            mat = bpy.data.materials.new(leatherMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        for node in nodes :
            if node.name == 'Diffuse BSDF' :
                print("it is Diffuse BSDF")
                shaderNodeBsdfDiffuseNode = node
            elif node.name == 'Material Output':
                print("it is output material")
                matOutputShader = node
        #add material output
        if matOutputShader is None :
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (1030,230)
        #add ShaderNodeMixShader
        shaderNodeMixShaderNode = nodes.new('ShaderNodeMixShader')
        shaderNodeMixShaderNode.location = (870,370)
        #add ShaderNodeMixRGB
        shaderNodeMixRGBNode = nodes.new('ShaderNodeMixRGB')
        shaderNodeMixRGBNode.location = (690,100)
        #add ShaderNodeBsdfDiffuse
        if shaderNodeBsdfDiffuseNode is None :
            shaderNodeBsdfDiffuseNode = nodes.new('ShaderNodeBsdfDiffuse')
        shaderNodeBsdfDiffuseNode.location = (690,390)
        #add glossy BSDF
        shaderNodeBsdfGlossyNode = nodes.new('ShaderNodeBsdfGlossy')
        shaderNodeBsdfGlossyNode.location = (690,260)
        #add image texture
        shaderNodeTexImageNode = nodes.new('ShaderNodeTexImage')
        shaderNodeTexImageNode.location = (500,330)
        #add ShaderNodeMapping
        shaderNodeMappingNode = nodes.new('ShaderNodeMapping')
        shaderNodeMappingNode.location = (160,360)
        #add ShaderNodeTexCoord
        shaderNodeTexCoordNode = nodes.new('ShaderNodeTexCoord')
        shaderNodeTexCoordNode.location = (0,360)
        ##make links
        links.new(shaderNodeTexCoordNode.outputs['UV'],shaderNodeMappingNode.inputs['Vector'])
        links.new(shaderNodeMappingNode.outputs['Vector'],shaderNodeTexImageNode.inputs['Vector'])
        links.new(shaderNodeTexImageNode.outputs['Color'],shaderNodeBsdfDiffuseNode.inputs['Color'])
        links.new(shaderNodeTexImageNode.outputs['Color'],shaderNodeMixRGBNode.inputs['Color2'])
        links.new(shaderNodeBsdfDiffuseNode.outputs['BSDF'],shaderNodeMixShaderNode.inputs[1])
        links.new(shaderNodeBsdfGlossyNode.outputs['BSDF'],shaderNodeMixShaderNode.inputs[2])
        links.new(shaderNodeMixShaderNode.outputs['Shader'],matOutputShader.inputs['Surface'])
        links.new(shaderNodeMixRGBNode.outputs['Color'],matOutputShader.inputs['Displacement'])
        ##adjust argument
        shaderNodeMappingNode.scale.xyz = (3.0,3.0,3.0)
        shaderNodeBsdfGlossyNode.inputs['Roughness'].default_value = 0.072
        shaderNodeMixRGBNode.inputs['Fac'].default_value = 0.368
        shaderNodeMixRGBNode.inputs['Color1'].default_value = (0.015,0.015,0.015,1.0)
        shaderNodeMixShaderNode.inputs['Fac'].default_value = 0.038
        return {'FINISHED'}

class cartoonObjectOperator(OneKeyOperator):
    """The operator about stainless stell object create
        add a stainless stell material automatic on current
        selected object"""
    bl_idname = "object.toon_cartoon_operator"
    bl_label = "Make Cartoon"

    def execute(self,context):
        carttonMaterial = context.blend_data.materials.new("CartoonMaterial")
        bpy.ops.object.material_slot_add()
        context.active_object.material_slots[0].material = carttonMaterial
        carttonMaterial.name = "CartoonMaterial"
        mat = bpy.data.materials.get(carttonMaterial.name)
        if mat is None :
            mat = bpy.data.materials.new(carttonMaterial.name)
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        links = mat.node_tree.links
        for node in nodes :
            if node.name == 'Diffuse BSDF' :
                mat.node_tree.nodes.remove(node)
            elif node.name == 'Material Output':
                print("it is output material")
                matOutputShader = node
        #add material output
        if matOutputShader is None :
            matOutputShader = nodes.new('ShaderNodeOutputMaterial')
        matOutputShader.location = (1060,370)
        #add ShaderNodeAddShader
        shaderNodeAddShaderNode = nodes.new('ShaderNodeAddShader')
        shaderNodeAddShaderNode.location = (860,320)
        #add ShaderNodeBsdfToon
        shaderNodeBsdfToonNodeOne = nodes.new('ShaderNodeBsdfToon')
        shaderNodeBsdfToonNodeOne.location = (610,420)
        shaderNodeBsdfToonNodeTwo = nodes.new('ShaderNodeBsdfToon')
        shaderNodeBsdfToonNodeTwo.location = (620,220)
        #add ShaderNodeHueSaturation
        shaderNodeHueSaturationNode = nodes.new('ShaderNodeHueSaturation')
        shaderNodeHueSaturationNode.location = (400,220)
        obj = context.active_object
        if obj.cartoon_nodes_is_with_texture_flag :
            #add image texture and texture coordinate
            #add ShaderNodeTexImage
            shaderNodeTexImageNode = nodes.new('ShaderNodeTexImage')
            shaderNodeTexImageNode.location = (200,420)
            #add ShaderNodeTexCoord
            shaderNodeTexCoordNode = nodes.new('ShaderNodeTexCoord')
            shaderNodeTexCoordNode.location = (0,420)
        else :
            #add ShaderNodeRGB
            shaderNodeRGBNode = nodes.new('ShaderNodeRGB')
            shaderNodeRGBNode.location = (200,420)
        ##make links
        if obj.cartoon_nodes_is_with_texture_flag :
            links.new(shaderNodeTexCoordNode.outputs['UV'],shaderNodeTexImageNode.inputs['Vector'])
            links.new(shaderNodeTexImageNode.outputs['Color'],shaderNodeBsdfToonNodeOne.inputs['Color'])
            links.new(shaderNodeTexImageNode.outputs['Color'],shaderNodeHueSaturationNode.inputs['Color'])
        else :
            links.new(shaderNodeRGBNode.outputs['Color'],shaderNodeBsdfToonNodeOne.inputs['Color'])
            links.new(shaderNodeRGBNode.outputs['Color'],shaderNodeHueSaturationNode.inputs['Color'])
        links.new(shaderNodeHueSaturationNode.outputs['Color'],shaderNodeBsdfToonNodeTwo.inputs['Color'])
        links.new(shaderNodeBsdfToonNodeOne.outputs['BSDF'],shaderNodeAddShaderNode.inputs[0])
        links.new(shaderNodeBsdfToonNodeTwo.outputs['BSDF'],shaderNodeAddShaderNode.inputs[1])
        links.new(shaderNodeAddShaderNode.outputs['Shader'],matOutputShader.inputs['Surface'])
        ##adjust argument
        shaderNodeHueSaturationNode.inputs['Value'].default_value = 0.8
        shaderNodeBsdfToonNodeOne.inputs['Size'].default_value = 0.6
        shaderNodeBsdfToonNodeOne.inputs['Smooth'].default_value = 0.2
        shaderNodeBsdfToonNodeTwo.inputs['Size'].default_value = 0.3
        shaderNodeBsdfToonNodeTwo.inputs['Smooth'].default_value = 0.02
        return {'FINISHED'}

class OneKeyToolsPanel(bpy.types.Panel) :
    """build a panel to run one key cloth obj make.
        Please note that all function must be avilable
        under cycles render mode."""
    bl_idname = "panel_one_key_tools"
    bl_label = "One Key Tools Panel"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "object"
    def draw(self,context):
        ui_layout = self.layout
        ### 
        ui_row = ui_layout.row()
        ui_row.label(text="make a plane become cloth(must under cycles mode)",
                        icon="UV_VERTEXSEL")
        ###
        ui_row = ui_layout.row()
        ui_row.operator(operator="object.plane_cotton_operator",
                        text="become cotton")
        ###
        ui_row = ui_layout.row()
        ui_row.label(text="make a cup model become glass cup(must under cycles mode)",
                        icon="UV_VERTEXSEL")
        ###
        ui_row = ui_layout.row()
        ui_row.operator(operator="object.glass_cup_operator",
                        text="become glass cup")
        ###
        ui_row = ui_layout.row()
        ui_row.label(text="make object have jelly material(must under cycles mode)",
                        icon="UV_VERTEXSEL")
        ###
        ui_row = ui_layout.row()
        ui_row.operator(operator="object.jelly_operator",
                        text="become jelly")
        ###
        ui_row = ui_layout.row()
        ui_row.label(text="make object have stainless steel material(must under cycles mode)",
                        icon="UV_VERTEXSEL")
        ###
        ui_row = ui_layout.row()
        ui_row.operator(operator="object.stainless_stell_operator",
                        text="become stainless steel")
        ###
        ui_row = ui_layout.row()
        ui_row.label(text="make object have leather material(must under cycles mode and you should have a leather texture)",
                        icon="UV_VERTEXSEL")
        ###
        ui_row = ui_layout.row()
        ui_row.operator(operator="object.leather_operator",text="become leather")
        ###
        ui_row = ui_layout.row()
        ui_row.label(text="make object have toon shader material(must under cycles mode)",icon="UV_VERTEXSEL")
        ###
        obj = context.active_object
        ui_row = ui_layout.row()
        ui_row_row = ui_row.row()
        ui_row_row.prop(obj,"cartoon_nodes_is_with_texture_flag")
        ui_row_row = ui_row.row()
        ui_row_row.operator(operator="object.toon_cartoon_operator",text="become cartoon")

def create_cotton_script_file():
    lines = [];
    lines.append("import bpy\n")
    lines.append("cloth = bpy.context.cloth\n\n")
    lines.append("cloth.settings.air_damping = 1.0\n")
    lines.append("cloth.settings.bending_stiffness = 0.5\n")
    lines.append("cloth.settings.mass = 0.30000001192092896\n")
    lines.append("cloth.settings.quality = 5\n")
    lines.append("cloth.settings.spring_damping = 5.0\n")
    lines.append("cloth.settings.structural_stiffness = 15.0\n")
    return lines

def create_tmp_file():
    #maybe we should get a path to store our tmp file
    global tmp_cotton_file_path
    tmp_cotton_file_path = os.getcwd()
    #and make a dir and a file
    print("tmp_cotton_file_path:",tmp_cotton_file_path)
    tmp_cotton_file_path += "/my_cotton.py"
    #open file to write
    fp = open(tmp_cotton_file_path,"w")
    #
    fp.writelines(create_cotton_script_file())
    fp.close()

def destroy_tmp_file():
    #remove file
    try :
        os.remove(tmp_cotton_file_path)
    except os.error:
        pass

def register():
    create_tmp_file()
    bpy.types.Object.cartoon_nodes_is_with_texture_flag = bpy.props.BoolProperty(name="is with texture",
                                            description="make cartoon material to object,if you also have a texture you can mix it by enable 'is with texture'",
                                            default=False);
    bpy.utils.register_class(OneKeyOperator)
    bpy.utils.register_class(PlaneBecomeCottonOperator)
    bpy.utils.register_class(GlassCupObjectOperator)
    bpy.utils.register_class(JellyObjectOperator)
    bpy.utils.register_class(stainlessSteelObjectOperator)
    bpy.utils.register_class(leatherObjectOperator)
    bpy.utils.register_class(cartoonObjectOperator)
    bpy.utils.register_class(OneKeyToolsPanel)
    

def unregister():
    destroy_tmp_file()
    del bpy.types.Object.cartoon_nodes_is_with_texture_flag
    bpy.utils.unregister_class(OneKeyOperator)
    bpy.utils.unregister_class(PlaneBecomeCottonOperator)
    bpy.utils.unregister_class(GlassCupObjectOperator)
    bpy.utils.unregister_class(JellyObjectOperator)
    bpy.utils.unregister_class(stainlessSteelObjectOperator)
    bpy.utils.unregister_class(leatherObjectOperator)
    bpy.utils.unregister_class(cartoonObjectOperator)
    bpy.utils.unregister_class(OneKeyToolsPanel)
    


if __name__ == "__main__":
    register()
